import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;


/**
 The Class VendingMachineTest.
 **/
public class VendingMachineTest {

        //UserVendingMachine Object
      
        public VendingMachine UserVendingMachine;
        
       
        /** VendingMachineItem Object - Lays*/
        private VendingMachineItem Lays;
        
       
        /** VendingMachineItem Object - Nutffles*/
        private VendingMachineItem Nutffles;
        
        /** VendingMachineItem Object - coke*/
        
        private VendingMachineItem coke;
        
        private VendingMachineItem pepsi;
        
        /** The item array. */
        private VendingMachineItem[] itemArray;
        
        /** The Constant NUM_SLOTS. */
        public static final int NUM_SLOTS = 4;
        
        /**
          Set up the Test Run by creating Objects.
          @throws Exception the exception
         
         **/
        @Before
        public void setUp() throws Exception {
                UserVendingMachine = new VendingMachine();
                Lays = new VendingMachineItem("Lays Chips", 1.00);
                Nutffles = new VendingMachineItem("Nutffles", 3.50);
                coke =  new VendingMachineItem("Coke", 1.75);
                pepsi = new VendingMachineItem("Pepsi", 1.50);
                
                itemArray = new VendingMachineItem[NUM_SLOTS];
                for (int i = 0; i < NUM_SLOTS; i++) {
                                itemArray[i] = null;
                }
        }

        /**
          Tear down.
           @throws Exception the exception
         **/
        @After
        public void tearDown() throws Exception {
        }

        
        
        /**
          It will test if item is added to Correct Slot.
         Postcondition: Test will be true if the items slot and the expected slot for the item are same.
         **/
        @Test public void testAddItem() {
                UserVendingMachine.addItem(Lays, VendingMachine.D_CODE);
                assertEquals(Lays,UserVendingMachine.getItem(VendingMachine.D_CODE));
        }

        
        /**
         It will test the add item for wrong item in a specified slot.
         Postcondition: Test will be false if the items slot and the expected slot are different.
         **/
        
        @Test public void testAddItem_WroItem() {
                UserVendingMachine.addItem(Nutffles, VendingMachine.C_CODE);
                assertEquals(Lays,UserVendingMachine.getItem(VendingMachine.C_CODE));
        }

        /**
         It test the add item for already occupied slot.
          Postcondition: Test run will be true if the exception is correctly caught
          @throws VendingMachineException If trying to add item in a non-empty slot.
         */
        
    @Test
    public void testAddItem_FulledSlot() throws Exception{
                    UserVendingMachine.addItem(coke,"A");
                    UserVendingMachine.addItem(pepsi, "B");         
                    try{
                        UserVendingMachine.addItem(pepsi, "A");   
                    }catch(VendingMachineException e){
                        String msg = "Slot A" + " already occupied";
                        assertEquals(msg, e.getMessage());
                    }
        }
    
    

        /**
         It test add item for invalid code slot.
         Postcondition: Test run will be true if the exception is correctly caught 
         @throws VendingMachineException If it will try to add item in an invalid slot.
         **/
    @Test 
    public void testAddItem_InvaCode() throws Exception{
              
                try{
                        UserVendingMachine.addItem(coke,"X");
            }catch(VendingMachineException e){
                String msg="Invalid code for vending machine item";
                assertEquals(msg, e.getMessage());
            }
        }
    
    

        /**
         It test the add item for already occupied slot 
         Postcondition: Test run will be true when it try to add item to already occupied slot.
         @throws VendingMachineException If it try to add item in an invalid slot.
         */
    
    @Test 
    public void testAddItem_CheckMess() throws Exception{
        UserVendingMachine.addItem(coke,"D");
        try{
               UserVendingMachine.addItem(pepsi,"D");
        }
        catch(VendingMachineException e){
                String msg = "Slot D" + " already occupied";
                        assertEquals(msg, e.getMessage());
        }
        }
    
    
        /**
          Test remove item from a slot.
          Postcondition: The test will be true if the item removed is present in the slot mentioned.
         */
    
        @Test
        public void testRemoveItem(){
                UserVendingMachine.addItem(Nutffles, VendingMachine.A_CODE);
                assertEquals(Nutffles, UserVendingMachine.removeItem(VendingMachine.A_CODE));
        }
        
        /**
         It Test remove item from an empty slot.
         Postcondition: Test will be true true when the slot mentioned is empty while removing an item.
         * @throws VendingMachineException if try to remove item which is not present in the code.
         */
        @Test 
        public void testRemoveItem_EmptySlot() throws Exception {
                
                try{
                         UserVendingMachine.removeItem(VendingMachine.D_CODE);                    
            }catch(VendingMachineException e){
                String msg="Slot D is empty -- cannot remove item";
                assertEquals(msg, e.getMessage());
            }
        }


        /**
           Test remove item for invalid code slot.
         Postcondition: Test run will be true when trying to remove item from an invalid slot.
         * @throws VendingMachineException If try to remove item from an invalid slot.
         */
    @Test
    public void testRemoveItem_InvalidCode() throws Exception{
        try{
                UserVendingMachine.removeItem("X");
        }catch(VendingMachineException e){
                String msg="Invalid code for vending machine item";
                assertEquals(msg, e.getMessage());
        }
                    
        }
    
        /**
          Test remove item of the slot, and check if trying to remove an item which is not belong to that particular slot.
          Postcondition: Test run will be true when trying to remove the item from a different slot.
          @throws VendingMachineException If trying to purchase a removed item.
         */
        @Test
        public void testRemoveItem_VerifyItem1() throws Exception {
                UserVendingMachine.addItem(Nutffles,"A");
                try{
                        UserVendingMachine.removeItem(VendingMachine.B_CODE);     
            }catch(VendingMachineException e){
                String msg="Slot B is empty -- cannot remove item";
                assertEquals(msg, e.getMessage());
            }
                
                
        }
        
        /**
         Test remove item of the slot, and check if there is any item existing.
          Postcondition: Test run should return true when trying to purchase the item which is removed.
          @throws VendingMachineException If it try to purchase a removed item.
         **/
        @Test 
        public void testRemoveItem_VerifyItem2() throws Exception {
                UserVendingMachine.addItem(coke,"A");
                UserVendingMachine.insertMoney(3.00);
                UserVendingMachine.makePurchase(VendingMachine.A_CODE);
                try{
                        UserVendingMachine.removeItem(VendingMachine.A_CODE);
                }
                catch(VendingMachineException e){
                        String msg =  "Slot A " + "is empty -- cannot remove item";
                        assertEquals(msg, e.getMessage());
                }
        }
        
        /**
         It test insert money into the machine, and check the balance.
         Postcondition: Test run will be true if the expected balance matches the actual balance.
         **/
        
        @Test
        public void testInsertMoney_CheckBal() throws Exception{
                UserVendingMachine.insertMoney(3.00);
                UserVendingMachine.insertMoney(0.50);
                assertEquals(3.50, UserVendingMachine.getBalance(), 0.0002);
        }

        /**
         Test insert money of the slot for amount < 0
         Postcondition: An exception will be thrown for -ve amount.
         @throws VendingMachineException If amount <0
         */
        @Test
        public void testInsertMoney_NegativeAmount() throws Exception {
                
                try{
                        UserVendingMachine.insertMoney(-0.50);
                                
            }catch(VendingMachineException e){
                String msg="Invalid amount.  Amount must be >= 0";
                assertEquals(msg, e.getMessage());
            }
                
                                
        }
        

        /**
         Test insert money of the slot for amount = 0
         Postcondition: Test run will be true if exception is occurred for negative amount while inserting money 
          @throws VendingMachineException If amount <0
         */
        
        @Test 
        public void testInsertMoney_ZAmount() throws Exception{
                UserVendingMachine.insertMoney(0.00);
        }
        
        /**
         Test insert money of the slot for amount > 0 and check balance
         Postcondition: Test case will fail if the inserted money doesn't match the balance
         */
        @Test 
        public void testInsertMoney_WrongBal() {
                UserVendingMachine.insertMoney(2.00);
                UserVendingMachine.insertMoney(0.50);
                assertEquals(2.75, UserVendingMachine.getBalance(), 0.0002);
        }
        
        
        /**
         It test get balance.
         **/
        @Test
        public void testGetBalance() {
                assertEquals(0.00, UserVendingMachine.getBalance(), 0.0002);
        }
        
        
        /**
          Test get balance with change.
         **/
        @Test
        public void testGetBalance_Wchange() {
                assertEquals(0.00, UserVendingMachine.getBalance(), 0.0002);
                UserVendingMachine.insertMoney(4.00);
                assertEquals(4.0, UserVendingMachine.getBalance(), 0.0002);
        }

        

        /**
         It Test make purchase for enough money and purchase item.
          Post Condition: Test case will run if the balance and the money in the Vending machine matches.
         **/
        @Test
        public void testMakePurchase_Goods() {
                UserVendingMachine.addItem(coke,"A");
                UserVendingMachine.insertMoney(4.00);
                UserVendingMachine.makePurchase("A");
                
                assertEquals(3.20, UserVendingMachine.getBalance(), 0.0001);
        }


        /**
         * Test make purchase for NOT enough money and purchase item.
         * Post Condition: Test case will fail if the balance is zero.
         */
        @Test
        public void testMakePurchase_ZeroBal() {
                UserVendingMachine.addItem(coke,"A");             
                assertFalse(UserVendingMachine.makePurchase("A"));
        }

        /**
         * Test make purchase for NOT enough money and purchase item.
         * Post Condition: Test case will fail if the balance is not sufficient.
         */
        @Test
        public void testMakePurchase_NenoughBal() {
                UserVendingMachine.addItem(coke,"A");
                UserVendingMachine.insertMoney(0.20);
                
                assertFalse(UserVendingMachine.makePurchase("A"));
        }
        
        /**
         * Test make purchase for an empty slot.
         * Post Condition: Test case will fail if there is no item in the specified slot.
         */
        @Test
        public void testMakePurchase_EmpSlot() {
                
                UserVendingMachine.insertMoney(0.10);
                assertFalse(UserVendingMachine.makePurchase("A"));
        }
        
        /**
         * Test return change.
         */
        @Test
        public void testRChange_goods() {
                UserVendingMachine.insertMoney(1.00);
                assertEquals(1.00, UserVendingMachine.returnChange(), 0.0002 );
                assertEquals(0.00, UserVendingMachine.getBalance(), 0.0002);
        }

        /**
         * Test return change for Zero Balance.
         */
        @Test
        public void testRChange_ZeroBal() {
                assertEquals(0.00, UserVendingMachine.returnChange(), 0.0002);
                assertEquals(0.00, UserVendingMachine.getBalance(), 0.0002);
        }
        
}